滲透測試重新打底(3.3)--論Web入侵之任意檔案上傳漏洞


Posted by kahatrix on 2023-09-22

Arbitrary File Upload

大家好,這一章節講的是任意檔案上傳,首先講一下任意檔案上傳的一些基本概念。任意檔案上傳的漏洞是一種非常常見的網路安全漏洞,它允許攻擊者上傳任意檔案到目標系統,這當然會造成很嚴重的後果,例如說一些未經授權的存取,或是執行一些惡意的程式碼,例如說一個Malware、Reversal、還有一些敏感資料的洩漏,或是RCE等等。

網路程式中產生任意檔案上傳漏洞漏洞的原因主要有兩點,一個是不正確的驗證和授權,也就是說一個Web Application在處理檔案上傳的時候沒有適當的驗證或是授權使用者的身份和權限,這就讓我們這種攻擊者可以繞過一些驗證機制來上傳任意的檔案;再來就是不安全的檔案處理,Web Application在處理上傳檔案的時候,沒有適當的檢查或是限制檔案的類型和內容,讓攻擊者可以上傳包含惡意程式碼的檔案,然後在伺服器上執行。

上傳跟執行是兩件事,因為你也許可以上傳一個PHP Reverse Shell、PHP Web Shell或是各種各樣的東西,但不見得可以Trigger它。例如說大家可能有遇過一種網站是你按下載的同時,它並不是直接Show在Browser或是在瀏覽器上面給你看,它會直接幫你下載下來,所以並不是每一個可以任意檔案上傳的地方都可以進行Trigger來拿到一個Web Shell或是等等的。

在Response中你可以看得出來,如果是一個你上傳Web Shell之後,對那個Web Shell的路徑做存取時,如果它的Response是一串PHP Code,那其實反而代表它是沒有被執行的,它就是印一段具有PHP程式碼的一個純文字檔而已。其實也就代表它是把那個PHP的程式碼下載給你,因為它在Web裡面的呈現方式並不是下載,而是會在Response裡面讓你去下載。

任意檔案上傳的影響很大,也會伴隨著其他漏洞產生一些組合技,第一個是上傳Web Shell再搭配一些Directory的Traversal,就是路徑遍歷。因為有時候我們上傳一個WebShell之後,它可能有一個上傳的介面,但是你其實完全不知道它上傳到哪裡去了,它就只有上傳,沒有給你瀏覽或是查看的部分,那有時候就很難知道它實際上到底被上傳到網站的哪一個位置;或是說網站的功能上它到底可不可以去瀏覽你所上傳的Web Shell。

第二個是上傳釣魚文件讓其他用戶或是管理者受到危害,我在進行一次滲透測試任務的時候就遇到類似這樣的問題,它是一個任意檔案上傳再加一個XSS,它會自動的寄到某一個管理者的Mailbox裡面,所以當他如果點擊要去查看這個東西的時候,就可能因為XSS的關係然後再這樣任意檔案上傳,所以把一些XSS,應該說Document Cookie,就是在JavaScript裡面的Document Cookie送回來給我,然後讓我可以去做到Account Takeover也就是帳戶接管,偷取管理員的Cookie進行無密碼登入的動作。再來是上傳來觸發其他Application的漏洞,例如像Buffer overflow,塞了一大堆的AAA字串,可能讓系統Crash或是執行一些我們熟知的一些Stack Overflow的一些事情,這都曾經發生過。

又例如我們在一個網站上它設計一個上傳大頭貼的功能,但是沒有對上傳的副檔名進行限制,這時候只要使用者上傳一行該網站使用的程式語言的Web Shell,就可以取得Reverse Shell。有些經典的像CTF裡面比較常出現的就是PHP,我們就可以上傳一個PHP的Web Shell,像這PHP Echo System,那Echo是印出來的意思,System就是基本上有點像Shell Exec,就是執行這段程式的意思。

<?php echo system($_GET['cmd']); ?>
<?php echo system($_POST['cmd']); ?>
<?php echo system($_REQUEST['cmd']); ?>

繼續看上面的三行不同的程式碼。在PHP裡面Request有幾種用法,這邊可以替換成GET,那它的使用方法就變成像是在GET method裡面的一個Query裡面夾帶一個CMD的東西。例如說我們可能可以輸入這樣的網址:

如果我們今天上傳一個ls,然後把Request改成GET的話,它就只能在這個參數列使用CMD去進行Injection。或是說用POST,那POST當然就在POST Data裡面。

例如說我們來建構一個Request:

<?php echo system($_GET['cmd']); ?>

我們只能在GET的Request中的Parameter的Value(下圖中的ls就是value)進行Common Injection的動作:

<?php echo system($_GET['cmd']); ?>

<?php echo system($_GET['cmd']); ?>

按右鍵再按下圖紅底線,把GET換成POST:

<?php echo system($_POST['cmd']); ?>

我們只能在POST的Request中的Parameter的Value(下圖中的ls就是value)進行Common Injection的動作:

第三種這個Request就比較特別一點,GET或是POST都可以用。也就是說如果你今天上傳一個第三個這種類型的Web Shell的話,你既可以在上圖下方底線處進行Common Injection或是寫一個Reversal進去,你也可以在這個URI裡面的Parameter裡面進行注入的動作。

一般來說我們建議就是直接使用Request會比較好,或是至少使用POST。這是因為在一些特殊的狀況下,如果你在單純使用GET Method,然後使用CMD這個Parameter去進行的話,可能會因為一些Encode跟一些長度的原因而Fail掉,所以通常會建議使用POST Data的方式傳遞我們想要執行的指令等等的。

還有ASP,它寫的方法就是:

<% eval request("cmd") %>

那Web Shell有非常非常多種,但要注意的是並不是每一種程式語言都可以拿來上傳Web Shell,應該說有一些語言會特別苛刻,因為有一些還需要compile才可以執行。假設我們今天用Go寫一個網站,然後它有任意檔案上傳漏洞,它可以上傳一個Go的Web Shell上去,可是它沒有進行compile就不會直接就執行,那這時候我們可以做的事情就是直接上傳一個Reversal(不是Web Shell了),因為我們用Go撰寫的Application不可能上傳PHP或ASP或JSP等等的Web Shell。

也有可能會遇到會限制副檔名的狀況,那如果單純只是檢查副檔名或是對上傳檔案進行限制的話,也有很多bypass的方式。假設我們今天要上傳一個PHP的Reversal,可是它不允許我們直接上傳PHP,這時候我們就可以先寫一個點PHP在一個副檔名裡面然後再點PNG,這樣在一些Application裡面,它可能會從最後面的那個字眼去去判斷說它是什麼樣的檔案類型。在這種狀況下,它會把這個副檔名的檔案當作一個PNG,所以就允許它上傳。或是說如果它只限制PHP的話,那我們可以用PHP5、PHT,PHTML這種方式都可以達到一樣的效果。

也可能它不是單純只用副檔名,或是說它不檢查副檔名,而是去讀取使用者發來的HTTP header中的content type進行判斷,例如說content type的image gif。那因為這個本身就等於是它信任user request中的input,所以非常好繞過,只要把content type中的value寫成網站可以接受的類型就可以了。如果我們單純只上傳一個PHP的web shell之類的話,它content type可能會是一個Application,HTTP,PHP等等的,就是你上傳的時候它會自動產生一個對應的content type的方式,那我們可以手動的透過Burp攔截,把它修改成網站可以接受的類型就可以了。

或是有可能不檢查上面這兩項,而是透過檢查user的post request的data中進行判斷。例如說我們今天post一個data它可能是一個reversal或是一張圖片。

我們剛剛有說這邊有一個content type:

那我們可以把它改成假設是GIF:

那如果它不認這種方式,也不去檢查副檔名,而是檢查它的header(就是magic number)的話,那我們知道說像這種圖片的類型通常會從開頭去判斷是什麼樣的檔案類型,例如說一個GIF的話,可能是GIF87A然後後面接著一串的圖片binary之類的東西:

這個地方你可以從這邊去找到,它有一個它的format,那PNG跟JPG都有各自的magic number。那這種方式就可以讓一個有漏洞的Operation把這個判斷成為一個GIF的檔案或是圖片。

再來想要介紹一個滿特別的,偶爾就會出現的一個漏洞。在介紹這個put method之前,想先大概的簡介一下一些method的東西。一個request當中,我們剛有看到一個用Burp suite它攔下來的一個request,我們直接從這邊去看:

GET / HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
X-Client-Data: CKLbygE=
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

這個request包含很多訊息,先以對一個Google發起的request來舉例。第一個這個get,是屬於叫做method的東西。再來是path,就是你在後面會接著資源的路徑。

再來會有host跟user agent跟一大堆其他有的沒的的header。如果有post data的話,它會出現在上圖第14行。

那method的定義,是目標索取資訊的方式,常見的有以下這些:

  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH

get就是最常一般我們瀏覽網站使用,我們瀏覽例如說google.com的時候,我們去進入它的首頁,通常都會先送一個get request出去,去索取它首頁的資源回來。那它就會response,可能是html,可能是javascript的資源進來。

再來是head,head跟get其實很像,只是差別在於說你使用head method它完全不會有任何的response,那我們可以來看一下。首先一樣用google.com做一個get request看看:

我們可以看到反藍處會回一大堆的http的一些資訊跟一個javascript的東西,就是response。給它head,會顯示一個200ok就跟get request一樣,但它卻沒有返回任何的response:

那header跟get主要差別在於一個有response data,一個沒有。

post data基本上就是,原本會在這邊存取的資源,例如說?file=123.txt:

如果使用post data的話(點一下上圖的Change request method),它就會出現在下面的地方:

那我們稱從第12行開始的這個區塊叫做post data。

put是一個蠻特殊的method,現在比較少用。一般我們現在都用post取代put,那基本上它的概念,是它把一個資源的raw data直接放上去。例如說這邊是一個123456789,然後直接把這個東西放進這個網站的路徑。例如說我們把123456789,put到google.com的一個叫file的地方。接下來這個網站就會在file底下出現一個123456789的raw data或可能是一個檔案,這是看網站的實作。

delete的話剛好跟put相反,它就是刪除網站上的資源。那其實put跟delete現在很少使用,一般都使用post data進行。因為put跟delete會造成一些問題,例如delete的話會直接刪除網站上的資源,而且它有時候很難驗證。所以我們一般會使用post data,然後裡面會有動作(action)。在post data裡面可能會有一個action叫做delete,那這樣它比較方便,它從header或是從其他地方判斷使用者的授權,或它有沒有刪除東西等等的。

那connect、trace、patch跟option的話,前三個比較少用。option的話,它就只是查看網站上可以使用的method有哪些。如果一個允許option的網站的話,它會在response的地方告訴你使用哪個method,那通常看到的是可以用get、head、post、option,這幾個是比較常見的。connect跟trace的話就先不講,有興趣的話可以直接搜尋http method。

patch的話它跟資料庫裡面SQL指令中的update有點像,就是更新已有的資料。例如說我們剛剛在file的這個patch裡面放了一個123456789,現在想要把它改成123,那我們就可以使用patch的方法,把123456789變成123。

如果可以put,然後它又沒有去檢查你的認證的話,就會產生一個蠻嚴重的資料漏洞,就是你可以直接放web shell進去。我們可以上傳一個惡意的payload或是一個web shell或檔案來達成rce或是code execution。

例如說我們在一個使用PHP的網站中,傳入下面的request:

PUT /upload.php HTTP/1.1
Host: example.com
...

filename=web_shell.php&&content="<php echo system(_REQUEST['cmd']); ?>"

我們先使用put method對upload.php上傳一個叫filename=web_shell.php,然後content,也就是內容,是php echo system(_REQUEST['cmd']);的資料,這樣我們就可以在request body中寫入一個PHP的web shell。成功傳入之後就可以用get method通過相對應的路徑呼叫,呼叫這個web shell再加上reverse shell,來取得我們的initial access或是說initial foothold。

當然我們不管是put或是任意檔案上傳,網站開發者可能都會進行一些限制,除了我們剛剛介紹的用php file、.jpg也有其他幾種方法,,不管是用put或是任意檔案上傳漏洞都可以通用,就是大小寫然後用php file再加大小寫,或是用png再加phtml這樣子,或是說在後面再加一個空格,%20是一個空格。url encode加上一個點再一個反斜線,或是一個%00。%00是一個null byte,它在一些C++或是一些PHP的程式會屬於一個程式的斷點。由於web application裡面有一些recover機制,它不會讓整個網站shut down,那所以它會把後面這些字串ignore掉之後上傳php。對於server來說,它會自動扣掉後面的文字,就是.png的部分。但是它在網站應用程式檢查的時候,它會看到.png,那任意檔案上傳的部分就到這裡結束。

黑名單
或是檢查非法副檔名如是否包含.php等等,這時候可以用一些方法來bypass:

shell.Php.png
shell.PHp5.jpg
shell.png.phtml
白名單
例如檢查檔案結尾是否為允許的類型如.jpeg,.gif,基本的bypass如下:

shell.php.jpeg
shell.png.php
或是

shell.php%20
shell.php.\
shell.php%00.png # null byte對於server來說會自動扣掉後面的文字.png

在OSCP裡面我們常常會看到的upload點,通常會是用gobuster、dirb buster,去爆破目錄取得的一個叫upload的資料夾。你會猜測說這個網站裡面可能會有一個upload功能,那你接下來就可能尋找說是不是有大頭貼或是它是不是可以上傳。例如說它是一個履歷的網站,那它會有一個可以投遞履歷的介面跟一個function,讓你可以進行檔案上傳。

其實我們檔案上傳的時候,如果我們可以指定一個filename等於什麼的話,就像我們下面這樣子的:

我們可以直接進行一些類似LFI的動作,就是我們把web_shell.php去掉之後,換成
../../etc/password這樣子,所以可以達到一些敏感資料洩漏。










Related Posts

[FE302] React 基礎 - hooks 版本 (性能優化)

[FE302] React 基礎 - hooks 版本 (性能優化)

Linux kernel feature

Linux kernel feature

1. ECMAScript - 前言

1. ECMAScript - 前言


Comments